﻿//-----------------------------------------------------------------------
// <copyright file="SlimDXControl.xaml.cs" company="Flaxen Geo">
//    Copyright (c) 2011, Michael P. Gerlek.  All rights reserved.
// </copyright>
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, 
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of Flaxen Geo nor the names of its contributors may be 
//   used to endorse or promote products derived from this software without 
//   specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
// THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------

namespace Flaxen.SlimDXControlLib
{
    using System;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;

    /// <summary>
    /// SlimDXControl is a WPF control that contains a D3DImage in it and handles all the DirectX management
    /// code for you.  Users set the Renderer property to point to their own D3D rendering code.
    /// </summary>
    public partial class SlimDXControl : ContentControl
    {
        private D3DImageSlimDX m_d3dImageSlimDX;
        private RenderEngine m_renderEngine;

        /// <summary>
        /// Initializes a new instance of the SlimDXControl class.
        /// </summary>
        public SlimDXControl()
        {
            Loaded += new RoutedEventHandler(SlimDXControl_Loaded);
            SizeChanged += new SizeChangedEventHandler(SlimDXControl_SizeChanged);
            Dispatcher.ShutdownStarted += new EventHandler(Dispatcher_ShutdownStarted);

            InitializeComponent();
        }

        /// <summary>
        /// After you create your RenderEngine object, call this routine to register the engine
        /// with the control.  The control will then assume responsibility to managing the engine.
        /// including disposal.
        /// </summary>
        /// <param name="engine">the RenderEngine object to associate with this control</param>
        public void RegisterRenderer(RenderEngine engine)
        {
            m_renderEngine = engine;
            if (m_renderEngine != null)
            {
                m_renderEngine.Initialize(this);
            }
        }

        /// <summary>
        /// Shuts down and cleans up the D3D world.
        /// Note there is no Closing event we can hook on to, so we rely on this public method which users of the control can call themselves.
        /// (We backstop the user calling this via the Dispatcher Shutdown event -- see 
        /// http://geekswithblogs.net/cskardon/archive/2008/06/23/dispose-of-a-wpf-usercontrol-ish.aspx)
        /// </summary>
        private void ShutdownD3D()
        {
            if (m_d3dImageSlimDX != null)
            {
                m_d3dImageSlimDX.Dispose();
                m_d3dImageSlimDX = null;
            }

            if (m_renderEngine != null)
            {
                m_renderEngine.Dispose();
                RegisterRenderer(null);
            }
        }

        private void SlimDXControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (DesignerProperties.GetIsInDesignMode(this))
            {
                return;
            } 
            
            int width = (int)e.NewSize.Width;
            int height = (int)e.NewSize.Height;
            m_renderEngine.Reinitialize(width, height);

            if (m_d3dImageSlimDX != null)
            {
                SlimDX.Direct3D10.Texture2D texture = m_renderEngine.SharedTexture;
                m_d3dImageSlimDX.SetBackBufferSlimDX(texture);
            }

            return;
        }

        private void Dispatcher_ShutdownStarted(object sender, EventArgs e)
        {
            ShutdownD3D();
        }

        private void SlimDXControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (DesignerProperties.GetIsInDesignMode(this))
            {
                return;
            }

            m_d3dImageSlimDX = new D3DImageSlimDX();
            m_d3dImageSlimDX.IsFrontBufferAvailableChanged += OnIsFrontBufferAvailableChanged;

            x_image.Source = m_d3dImageSlimDX;

            // BUG: this never gets caught, alas
            if (m_renderEngine == null)
            {
                throw new InvalidOperationException("scene is null");
            }

            SlimDX.Direct3D10.Texture2D texture = m_renderEngine.SharedTexture;

            m_d3dImageSlimDX.SetBackBufferSlimDX(texture);
            BeginRenderingScene();
        }

        private void OnRendering(object sender, EventArgs e)
        {
            m_renderEngine.Render();
            m_d3dImageSlimDX.InvalidateD3DImage();
        }

        private void BeginRenderingScene()
        {
            if (m_d3dImageSlimDX.IsFrontBufferAvailable)
            {
                SlimDX.Direct3D10.Texture2D texture = m_renderEngine.SharedTexture;
                m_d3dImageSlimDX.SetBackBufferSlimDX(texture);
                CompositionTarget.Rendering += OnRendering;
            }
        }

        private void StopRenderingScene()
        {
            CompositionTarget.Rendering -= OnRendering;
        }

        private void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            // This fires when the screensaver kicks in, the machine goes into sleep or hibernate
            // and any other catastrophic losses of the d3d device from WPF's point of view
            if (m_d3dImageSlimDX.IsFrontBufferAvailable)
            {
                BeginRenderingScene();
            }
            else
            {
                StopRenderingScene();
            }
        }
    }
}
